home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 1.iso
/
toolbox
/
src
/
exampleCode
/
opengl
/
x+opengl
/
textfun.c
< prev
Wrap
C/C++ Source or Header
|
1996-11-11
|
25KB
|
846 lines
/* $Revision: 1.1 $ */
/* compile: cc -o textfun textfun.c -lXm -lXt -lGL -lXext -lX11 -lm */
/*
* textfun demonstrates pulling X bitmap fonts from the X server into an
* OpenGL client and converting the bitmaps into OpenGL display lists with
* transformable geometry. Text can then be displayed from any perspective
* in 3D.
*
* Motif is used for the user interface. The program renders OpenGL into a
* standard Motif drawing area and does not use any special OpenGL widget.
* Pull down menus with toggles and radio buttons are used. The animation is
* controled by X Toolkit work procs; iconfiying textfun will stop the work
* proc and resume it when the program is uniconified.
*
* Various fonts can be switched between. A number of the fonts are X scalable
* fonts demonstrating how the blocky nature of the text can be minimized
* with higher resolution fonts.
*
* Mark J. Kilgard
* mjk@sgi.com
* Silicon Graphics, Inc.
* March 7, 1994
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#ifdef IRIX_5_1_MOTIF_BUG_WORKAROUND
#include <sys/utsname.h>
#endif
#include <Xm/MainW.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/CascadeB.h>
#include <Xm/Frame.h>
#include <Xm/DrawingA.h>
#include <X11/keysym.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#ifdef DEBUG
#define GL_ERROR_CHECK() \
{ /* for help debugging, report any OpenGL errors that occur per frame */ \
GLenum error; \
while((error = glGetError()) != GL_NO_ERROR) \
fprintf(stderr, "GL error: %s, line %d\n", gluErrorString(error), __LINE__); \
}
#else
#define GL_ERROR_CHECK() { /* nothing */ }
#endif
typedef struct {
short width;
short height;
short xoffset;
short yoffset;
short advance;
char *bitmap;
} PerCharInfo, *PerCharInfoPtr;
typedef struct {
int min_char;
int max_char;
int max_ascent;
int max_descent;
GLuint dlist_base;
PerCharInfo glyph[1];
} FontInfo, *FontInfoPtr;
typedef struct {
char *name;
char *xlfd;
XFontStruct *xfont;
FontInfoPtr fontinfo;
} FontEntry, *FontEntryPtr;
static int dblBuf[] =
{
GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16,
GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
None
};
static int *snglBuf = &dblBuf[1];
static String fallbackResources[] =
{
"*sgiMode: true", /* try to enable IRIX 5.2+ look & feel */
"*useSchemes: all", /* and SGI schemes */
"*title: OpenGL text transformation",
"*glxarea*width: 300", "*glxarea*height: 300", NULL
};
static FontEntry fontEntry[] =
{
{"Fixed", "fixed", NULL, NULL},
{"Utopia", "-adobe-utopia-medium-r-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
{"Schoolbook", "-adobe-new century schoolbook-bold-i-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
{"Rock", "-sgi-rock-medium-r-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
{"Rock (hi-res)", "-sgi-rock-medium-r-normal--50-*-*-*-p-*-iso8859-1", NULL, NULL},
{"Curl", "-sgi-curl-medium-r-normal--20-*-*-*-p-*-*-*", NULL, NULL},
{"Curl (hi-res)", "-sgi-curl-medium-r-normal--50-*-*-*-p-*-*-*", NULL, NULL},
{"Dingbats", "-adobe-itc zapf dingbats-medium-r-normal--35-*-*-*-p-*-adobe-fontspecific", NULL, NULL}
};
#define NUM_FONT_ENTRIES sizeof(fontEntry)/sizeof(FontEntry)
static char *defaultMessage[] =
{"OpenGL ", "rocks into", "the Future!"};
#define NUM_DEFAULT_MESSAGES sizeof(defaultMessage)/sizeof(char*)
Display *dpy;
GLboolean doubleBuffer = GL_TRUE, motion = GL_FALSE, rotation = GL_FALSE,
wobbling = GL_FALSE, made_current = GL_FALSE, dollying = GL_TRUE;
XtAppContext app;
XtWorkProcId workId = 0;
Widget toplevel = NULL;
Widget mainw, menubar, menupane, btn, cascade, frame, glxarea;
GLXContext cx;
XVisualInfo *vi;
Colormap cmap;
Arg menuPaneArgs[4], args[1];
GLfloat theta = 0, delta = 0;
GLfloat distance = 19, angle = 0, wobble_angle = 0;
GLuint base;
int numMessages;
char **messages;
void
draw(Widget w)
{
GLfloat red, green, blue;
int i;
glClear(GL_DEPTH_BUFFER_BIT);
/* paint black to blue smooth shaded polygon for background */
glDisable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glBegin(GL_POLYGON);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(-20, 20, -19);
glVertex3f(20, 20, -19);
glColor3f(0.0, 0.0, 1.0);
glVertex3f(20, -20, -19);
glVertex3f(-20, -20, -19);
glEnd();
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_FLAT);
glPushMatrix();
glTranslatef(0, 0, -distance);
glRotatef(angle, 0, 0, 1);
glRotatef(wobble_angle, 0, 1, 0);
glCallList(base);
glPopMatrix();
if (doubleBuffer)
glXSwapBuffers(dpy, XtWindow(w));
if (!glXIsDirect(dpy, cx))
glFinish(); /* avoid indirect rendering latency from
* queuing */
GL_ERROR_CHECK();
}
void
resize(Widget w, XtPointer data, XtPointer callData)
{
Dimension width, height;
/*
* It is possible for a drawing area widget's resize callback to be
* called before the window is realized, and therefore before we have
* made our OpenGL context to the window ID. So only let the glViewPort
* call happen if we really have "made_current".
*/
if (made_current) {
XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL);
glViewport(0, 0, (GLint) width, (GLint) height);
}
}
void
tick(void)
{
if (dollying) {
theta += 0.1;
distance = cos(theta) * 7 + 12;
}
if (rotation)
angle -= 6;
if (wobbling) {
delta += 0.1;
wobble_angle = sin(delta) * 40;
}
}
Boolean
animate(XtPointer data)
{
tick();
draw(glxarea);
return False; /* leave work proc active */
}
void
syncstate(void)
{
if (motion && (dollying || rotation || wobbling)) {
if (workId == 0)
workId = XtAppAddWorkProc(app, animate, NULL);
} else if (workId != 0) {
XtRemoveWorkProc(workId);
workId = 0;
}
}
void
toggle(void)
{
motion = !motion; /* toggle */
syncstate();
}
void
dolly(void)
{
dollying = !dollying; /* toggle */
syncstate();
}
void
rotate(void)
{
rotation = !rotation; /* toggle */
syncstate();
}
void
wobble(void)
{
wobbling = !wobbling; /* toggle */
syncstate();
}
void
quit(Widget w, XtPointer data, XtPointer callData)
{
exit(0);
}
void
input(Widget w, XtPointer data, XtPointer callData)
{
XmDrawingAreaCallbackStruct *cd = (XmDrawingAreaCallbackStruct *) callData;
char buf[1];
KeySym keysym;
int rc;
if (cd->event->type == KeyPress)
if (XLookupString((XKeyEvent *) cd->event, buf, 1, &keysym, NULL) == 1)
switch (keysym) {
case XK_space:
if (!motion) { /* advance one frame if not in motion */
tick();
draw(w);
}
break;
case XK_Escape:
exit(0);
}
}
void
map_state_changed(Widget w, XtPointer data, XEvent * event, Boolean * cont)
{
switch (event->type) {
case MapNotify:
syncstate();
break;
case UnmapNotify:
if (motion) {
XtRemoveWorkProc(workId);
workId = 0;
}
break;
}
}
/* #define REPORT_GLYPHS */
#ifdef REPORT_GLYPHS
#define DEBUG_GLYPH4(msg,a,b,c,d) printf(msg,a,b,c,d)
#define DEBUG_GLYPH(msg) printf(msg)
#else
#define DEBUG_GLYPH4(msg,a,b,c,d) { /* nothing */ }
#define DEBUG_GLYPH(msg) { /* nothing */ }
#endif
#define MAX_GLYPHS_PER_GRAB 512 /* this is big enough for 2^9 glyph character sets */
FontInfoPtr
SuckGlyphsFromServer(Display * dpy, Font font)
{
Pixmap offscreen;
XFontStruct *fontinfo;
XImage *image;
GC xgc;
XGCValues values;
int numchars;
int width, height, pixwidth;
int i, j;
XCharStruct *charinfo;
XChar2b character;
char *bitmapData;
int x, y;
int spanLength;
int charWidth, charHeight, maxSpanLength;
int grabList[MAX_GLYPHS_PER_GRAB];
int glyphsPerGrab = MAX_GLYPHS_PER_GRAB;
int numToGrab, thisglyph;
FontInfoPtr myfontinfo;
fontinfo = XQueryFont(dpy, font);
if (!fontinfo)
return NULL;
numchars = fontinfo->max_char_or_byte2 - fontinfo->min_char_or_byte2 + 1;
if (numchars < 1)
return NULL;
myfontinfo = (FontInfoPtr) malloc(sizeof(FontInfo) + (numchars - 1) * sizeof(PerCharInfo));
if (!myfontinfo)
return NULL;
myfontinfo->min_char = fontinfo->min_char_or_byte2;
myfontinfo->max_char = fontinfo->max_char_or_byte2;
myfontinfo->max_ascent = fontinfo->max_bounds.ascent;
myfontinfo->max_descent = fontinfo->max_bounds.descent;
myfontinfo->dlist_base = 0;
width = fontinfo->max_bounds.rbearing - fontinfo->min_bounds.lbearing;
height = fontinfo->max_bounds.ascent + fontinfo->max_bounds.descent;
maxSpanLength = (width + 7) / 8;
/*
* Be careful determining the width of the pixmap; the X protocol allows
* pixmaps of width 2^16-1 (unsigned short size) but drawing coordinates
* max out at 2^15-1 (signed short size). If the width is too large,
* we need to limit the glyphs per grab.
*/
if ((glyphsPerGrab * 8 * maxSpanLength) >= (1 << 15)) {
glyphsPerGrab = (1 << 15) / (8 * maxSpanLength);
}
pixwidth = glyphsPerGrab * 8 * maxSpanLength;
offscreen = XCreatePixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)),
pixwidth, height, 1);
values.font = font;
values.background = 0;
values.foreground = 0;
xgc = XCreateGC(dpy, offscreen, GCFont | GCBackground | GCForeground, &values);
XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
XSetForeground(dpy, xgc, 1);
numToGrab = 0;
if (fontinfo->per_char == NULL) {
charinfo = &(fontinfo->min_bounds);
charWidth = charinfo->rbearing - charinfo->lbearing;
charHeight = charinfo->ascent + charinfo->descent;
spanLength = (charWidth + 7) / 8;
}
for (i = 0; i < numchars; i++) {
if (fontinfo->per_char != NULL) {
charinfo = &(fontinfo->per_char[i]);
charWidth = charinfo->rbearing - charinfo->lbearing;
charHeight = charinfo->ascent + charinfo->descent;
if (charWidth == 0 || charHeight == 0) {
/* Still must move raster pos even if empty character */
myfontinfo->glyph[i].width = 0;
myfontinfo->glyph[i].height = 0;
myfontinfo->glyph[i].xoffset = 0;
myfontinfo->glyph[i].yoffset = 0;
myfontinfo->glyph[i].advance = charinfo->width;
myfontinfo->glyph[i].bitmap = NULL;
goto PossiblyDoGrab;
}
}
grabList[numToGrab] = i;
/* XXX is this right for large fonts? */
character.byte2 = (i + fontinfo->min_char_or_byte2) & 255;
character.byte1 = (i + fontinfo->min_char_or_byte2) >> 8;
/*
* XXX we could use XDrawImageString16 which would also paint the
* backing rectangle but X server bugs in some scalable font
* rasterizers makes it more effective to do XFillRectangles to clear
* the pixmap and XDrawImage16 for the text.
*/
XDrawString16(dpy, offscreen, xgc,
-charinfo->lbearing + 8 * maxSpanLength * numToGrab,
charinfo->ascent, &character, 1);
numToGrab++;
PossiblyDoGrab:
if (numToGrab >= glyphsPerGrab || i == numchars - 1) {
image = XGetImage(dpy, offscreen,
0, 0, pixwidth, height, 1, XYPixmap);
for (j = 0; j < numToGrab; j++) {
thisglyph = grabList[j];
if (fontinfo->per_char != NULL) {
charinfo = &(fontinfo->per_char[thisglyph]);
charWidth = charinfo->rbearing - charinfo->lbearing;
charHeight = charinfo->ascent + charinfo->descent;
spanLength = (charWidth + 7) / 8;
}
bitmapData = calloc(height * spanLength, sizeof(char));
if (!bitmapData)
goto FreeFontAndReturn;
DEBUG_GLYPH4("index %d, glyph %d (%d by %d)\n",
j, thisglyph + fontinfo->min_char_or_byte2, charWidth, charHeight);
for (y = 0; y < charHeight; y++) {
for (x = 0; x < charWidth; x++) {
/*
* XXX The algorithm used to suck across the font ensures
* that each glyph begins on a byte boundary. In theory
* this would make it convienent to copy the glyph into
* a byte oriented bitmap. We actually use the XGetPixel
* function to extract each pixel from the image which is
* not that efficient. We could either do tighter packing
* in the pixmap or more efficient extraction from the
* image. Oh well.
*/
if (XGetPixel(image, j * maxSpanLength * 8 + x, charHeight - 1 - y)) {
DEBUG_GLYPH("x");
bitmapData[y * spanLength + x / 8] |= (1 << (x & 7));
} else {
DEBUG_GLYPH(" ");
}
}
DEBUG_GLYPH("\n");
}
myfontinfo->glyph[thisglyph].width = charWidth;
myfontinfo->glyph[thisglyph].height = charHeight;
myfontinfo->glyph[thisglyph].xoffset = -charinfo->lbearing;
myfontinfo->glyph[thisglyph].yoffset = charinfo->descent;
myfontinfo->glyph[thisglyph].advance = charinfo->width;
myfontinfo->glyph[thisglyph].bitmap = bitmapData;
}
XDestroyImage(image);
numToGrab = 0;
/* do we need to clear the offscreen pixmap to get more? */
if (i < numchars - 1) {
XSetForeground(dpy, xgc, 0);
XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
XSetForeground(dpy, xgc, 1);
}
}
}
XFreeGC(dpy, xgc);
XFreePixmap(dpy, offscreen);
return myfontinfo;
FreeFontAndReturn:
XDestroyImage(image);
XFreeGC(dpy, xgc);
XFreePixmap(dpy, offscreen);
for (j = i - 1; j >= 0; j--) {
if (myfontinfo->glyph[j].bitmap)
free(myfontinfo->glyph[j].bitmap);
}
free(myfontinfo);
return NULL;
}
void
MakeCube(void)
{
/*
* No back side to the cube is drawn since the animation makes sure the
* back side can never be visible. The "wobble" function is constrained
* so not to rotate far enough around to reveal the back side.
*/
glNewList(1, GL_COMPILE);
glBegin(GL_QUAD_STRIP);
/* back left post */
glColor3f(6.0, 0.5, 0.5);
glVertex3f(0, 0, 0);
glVertex3f(0, 1, 0);
/* front left post */
glVertex3f(0, 0, 1);
glVertex3f(0, 1, 1);
glColor3f(1.0, 0.0, 0.0);
/* front right post */
glVertex3f(1, 0, 1);
glVertex3f(1, 1, 1);
/* back right post */
glColor3f(6.0, 0.5, 0.5);
glVertex3f(1, 0, 0);
glVertex3f(1, 1, 0);
glEnd();
glBegin(GL_QUADS);
/* top face */
glVertex3f(1, 1, 0);
glVertex3f(1, 1, 1);
glVertex3f(0, 1, 1);
glVertex3f(0, 1, 0);
/* bottom face */
glVertex3f(1, 0, 0);
glVertex3f(1, 0, 1);
glVertex3f(0, 0, 1);
glVertex3f(0, 0, 0);
glEnd();
glEndList();
}
void
MakeGlyphDisplayList(FontInfoPtr font, int c)
{
PerCharInfoPtr glyph;
char *bitmapData;
int width, height, spanLength;
int x, y;
if (c < font->min_char || c > font->max_char)
return;
if (font->dlist_base == 0) {
font->dlist_base = glGenLists(font->max_char - font->min_char + 1);
if (font->dlist_base == 0)
XtAppError(app, "could not generate font display lists");
}
glyph = &font->glyph[c - font->min_char];
glNewList(c - font->min_char + font->dlist_base, GL_COMPILE);
bitmapData = glyph->bitmap;
if (bitmapData) {
int oldx = 0, oldy = 0;
glPushMatrix();
glTranslatef(-glyph->xoffset, -glyph->yoffset, 0);
width = glyph->width;
spanLength = (width + 7) / 8;
height = glyph->height;
for (x = 0; x < width; x++) {
for (y = 0; y < height; y++) {
if (bitmapData[y * spanLength + x / 8] & (1 << (x & 7))) {
int y1, count;
/*
* Fonts tend to have good vertical repetion. If we find that
* the vertically adjacent pixels in the glyph bitmap are also enabled,
* we can scale a single cube instead of drawing a cube per pixel.
*/
for (y1 = y + 1, count = 1; y < height; y1++, count++) {
if (!(bitmapData[y1 * spanLength + x / 8] & (1 << (x & 7))))
break;
}
glTranslatef(x - oldx, y - oldy, 0);
oldx = x;
oldy = y;
if (count > 1) {
glPushMatrix();
glScalef(1, count, 1);
glCallList(1);
glPopMatrix();
y += count - 1;
} else {
glCallList(1);
}
}
}
}
glPopMatrix();
}
glTranslatef(glyph->advance, 0, 0);
glEndList();
}
GLuint
GetGlyphDisplayList(FontInfoPtr font, int c)
{
PerCharInfoPtr glyph;
if (c < font->min_char || c > font->max_char)
return 0;
if (font->dlist_base == 0)
XtAppError(app, "font not display listed");
return c - font->min_char + font->dlist_base;
}
MakeStringDisplayList(FontInfoPtr font, unsigned char *message, GLuint dlist)
{
unsigned char *c;
for (c = message; *c != '\0'; c++) {
MakeGlyphDisplayList(font, *c);
}
glNewList(dlist, GL_COMPILE);
for (c = message; *c != '\0'; c++) {
glCallList(GetGlyphDisplayList(font, *c));
}
glEndList();
}
int
GetStringLength(FontInfoPtr font, unsigned char *message)
{
unsigned char *c;
int ch;
int width = 0;
for (c = message; *c != '\0'; c++) {
ch = *c;
if (ch >= font->min_char && ch <= font->max_char) {
width += font->glyph[ch - font->min_char].advance;
}
}
return width;
}
SetupMessageDisplayList(FontEntryPtr fontEntry, int num, char *message[])
{
FontInfoPtr fontinfo = fontEntry->fontinfo;
GLfloat scaleFactor;
int totalHeight, maxWidth, height, width;
int i;
if (!fontinfo) {
fontinfo = SuckGlyphsFromServer(dpy, fontEntry->xfont->fid);
fontEntry->fontinfo = fontinfo;
}
height = fontinfo->max_ascent + fontinfo->max_descent;
maxWidth = 0;
for (i = 0; i < num; i++) {
MakeStringDisplayList(fontinfo, message[i], base + i + 1);
width = GetStringLength(fontinfo, message[i]);
if (width > maxWidth)
maxWidth = width;
}
#define SHRINK_FACTOR 25.0 /* empirical */
totalHeight = height * num - fontinfo->max_descent;
if (maxWidth > totalHeight) {
scaleFactor = SHRINK_FACTOR / maxWidth;
} else {
scaleFactor = SHRINK_FACTOR / totalHeight;
}
glNewList(base, GL_COMPILE);
glScalef(scaleFactor, scaleFactor, 1); /* 1 in Z gives glyphs constant depth */
for (i = 0; i < num; i++) {
glPushMatrix();
width = GetStringLength(fontinfo, message[i]);
glTranslatef(-width / 2.0, height * (num - i - 1) - totalHeight / 2.0, 0);
glCallList(base + i + 1);
glPopMatrix();
}
glEndList();
}
void
fontSelect(Widget widget, XtPointer client_data, XmRowColumnCallbackStruct * cbs)
{
XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *) cbs->callbackstruct;
FontEntryPtr fontEntry = (FontEntryPtr) cbs->data;
if (state->set) {
SetupMessageDisplayList(fontEntry, numMessages, messages);
if (!motion)
draw(glxarea);
}
}
void
neverCalled(void)
{
}
void
main(int argc, char *argv[])
{
int i;
#ifdef IRIX_5_1_MOTIF_BUG_WORKAROUND
/*
* XXX Unfortunately a bug in the IRIX 5.1 Motif shared library
* causes a BadMatch X protocol error if the SGI look&feel
* is enabled for this program. If we detect we are on an
* IRIX 5.1 system, skip the first two fallback resources which
* specify using the SGI look&feel.
*/
struct utsname versionInfo;
if(uname(&versionInfo) >= 0) {
if(!strcmp(versionInfo.sysname, "IRIX") &&
!strncmp(versionInfo.release, "5.1", 3)) {
toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
&fallbackResources[2], NULL, 0);
}
}
if(toplevel == NULL) {
toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
fallbackResources, NULL, 0);
}
#else
toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
fallbackResources, NULL, 0);
#endif
dpy = XtDisplay(toplevel);
/* find an OpenGL-capable RGB visual with depth buffer */
vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
if (vi == NULL) {
vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
if (vi == NULL)
XtAppError(app, "no RGB visual with depth buffer");
doubleBuffer = GL_FALSE;
}
for (i = 0; i < NUM_FONT_ENTRIES; i++) {
fontEntry[i].xfont = XLoadQueryFont(dpy, fontEntry[i].xlfd);
if (i == 0 && !fontEntry[i].xfont)
XtAppError(app, "could not get basic font");
}
fontEntry[0].fontinfo = SuckGlyphsFromServer(dpy, fontEntry[0].xfont->fid);
if (!fontEntry[0].fontinfo)
XtAppError(app, "could not get font glyphs");
/* create an OpenGL rendering context */
cx = glXCreateContext(dpy, vi, /* no display list sharing */ None,
/* favor direct */ GL_TRUE);
if (cx == NULL)
XtAppError(app, "could not create rendering context");
/* create an X colormap since probably not using default visual */
cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
vi->visual, AllocNone);
XtVaSetValues(toplevel, XtNvisual, vi->visual, XtNdepth, vi->depth,
XtNcolormap, cmap, NULL);
XtAddEventHandler(toplevel, StructureNotifyMask, False,
map_state_changed, NULL);
mainw = XmCreateMainWindow(toplevel, "mainw", NULL, 0);
XtManageChild(mainw);
/* create menu bar */
menubar = XmCreateMenuBar(mainw, "menubar", NULL, 0);
XtManageChild(menubar);
/* hack around Xt's ignorance of visuals */
XtSetArg(menuPaneArgs[0], XmNdepth, DefaultDepthOfScreen(XtScreen(mainw)));
XtSetArg(menuPaneArgs[1],
XmNcolormap, DefaultColormapOfScreen(XtScreen(mainw)));
/* create File pulldown menu: Quit */
menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 2);
btn = XmCreatePushButton(menupane, "Quit", NULL, 0);
XtAddCallback(btn, XmNactivateCallback, quit, NULL);
XtManageChild(btn);
XtSetArg(args[0], XmNsubMenuId, menupane);
cascade = XmCreateCascadeButton(menubar, "File", args, 1);
XtManageChild(cascade);
/* create Options pulldown menu: Motion, Dolly, Rotate, Wobble */
menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 2);
btn = XmCreateToggleButton(menupane, "Motion", NULL, 0);
XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) toggle, NULL);
XtManageChild(btn);
btn = XmCreateToggleButton(menupane, "Dolly", NULL, 0);
XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) dolly, NULL);
XtVaSetValues(btn, XmNset, True, NULL);
XtManageChild(btn);
btn = XmCreateToggleButton(menupane, "Rotate", NULL, 0);
XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) rotate, NULL);
XtManageChild(btn);
btn = XmCreateToggleButton(menupane, "Wobble", NULL, 0);
XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) wobble, NULL);
XtManageChild(btn);
XtSetArg(args[0], XmNsubMenuId, menupane);
cascade = XmCreateCascadeButton(menubar, "Options", args, 1);
XtManageChild(cascade);
XtSetArg(menuPaneArgs[2], XmNradioBehavior, True);
XtSetArg(menuPaneArgs[3], XmNradioAlwaysOne, True);
menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 4);
XtAddCallback(menupane, XmNentryCallback, (XtCallbackProc) fontSelect, NULL);
for (i = 0; i < NUM_FONT_ENTRIES; i++) {
btn = XmCreateToggleButton(menupane, fontEntry[i].name, NULL, 0);
XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) neverCalled, &fontEntry[i]);
if (i == 0)
XtVaSetValues(btn, XmNset, True, NULL);
if (!fontEntry[i].xfont)
XtSetSensitive(btn, False);
XtManageChild(btn);
}
XtSetArg(args[0], XmNsubMenuId, menupane);
cascade = XmCreateCascadeButton(menubar, "Font", args, 1);
XtManageChild(cascade);
/* create framed drawing area for OpenGL rendering */
frame = XmCreateFrame(mainw, "frame", NULL, 0);
XtManageChild(frame);
glxarea = XtCreateManagedWidget("glxarea", xmDrawingAreaWidgetClass,
frame, NULL, 0);
XtAddCallback(glxarea, XmNexposeCallback, (XtCallbackProc) draw, NULL);
XtAddCallback(glxarea, XmNresizeCallback, resize, NULL);
XtAddCallback(glxarea, XmNinputCallback, input, NULL);
/* set up application's window layout */
XmMainWindowSetAreas(mainw, menubar, NULL, NULL, NULL, frame);
XtRealizeWidget(toplevel);
/*
* Once widget is realized (ie, associated with a created X window), we
* can bind the OpenGL rendering context to the window.
*/
glXMakeCurrent(dpy, XtWindow(glxarea), cx);
made_current = GL_TRUE;
/* setup OpenGL state */
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearDepth(1.0);
glMatrixMode(GL_PROJECTION);
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 80);
glMatrixMode(GL_MODELVIEW);
MakeCube();
if (argv[1] != NULL) {
numMessages = argc - 1;
messages = &argv[1];
} else {
numMessages = NUM_DEFAULT_MESSAGES;
messages = defaultMessage;
}
base = glGenLists(numMessages + 1);
SetupMessageDisplayList(&fontEntry[0], numMessages, messages);
tick();
/* start event processing */
XtAppMainLoop(app);
}